import copy
import hashlib
import mimetypes
import os
import re
import warnings
from pathlib import Path
from typing import Any, Literal
from urllib.parse import unquote, urlparse
import pandas
import requests
import yaml
from dotenv import load_dotenv
from pydantic import BaseModel, Field
from ..dataclass.gdimData import GdimFieldMetadata, GdimMinIOFile, GdimProjectInfo, GdimTableMetaData, GdimTemplate, GdimWidgetDataStructure, StandardLayerUpdate, TemplateAppInfo
from ..dataclass.results import SingleResult
from ..dataclass.tables import TableCollection, TableData
from ..dataclass.terminologies import Units

current_dir: Any
config_file_path: Any
config: Any
DATA_TYPE_MAP: Any

def log_in(user_name: Any = None, password: Any = None, host: Any = None, gdim: bool = True) -> str:
    """
    Log in to GDIM and return the user token.
    
    Args:
        user_name: Username for login. If None, will try to read from .env file (GDIM_USERNAME)
        password: Password for login. If None, will try to read from .env file (GDIM_PASSWORD)
        host: API host URL. If None, uses default host
        gdim: Whether to use GDIM API (True) or alternative API (False)
    
    Returns:
        str: Authentication token
    
    Raises:
        Exception: If login fails or credentials are missing
    """
    ...
def log_in_zhengyuan(cid: int, user_name: Any = None, password: Any = None, host: Any = None) -> str:
    """Log in to GDIM and return the user token."""
    ...
def get_tpl_list(user_token: str, host: Any = None) -> dict[str, GdimTplMetadata]:
    """
    Get the list of templates according to the user token.
    
    Returns:
        dict[str, GdimTplMetadata]: The list of templates. Keys are both template names and template titles.
    """
    ...
def get_tpl_structure(user_token: str, tpl_id: Any = None, proj_id: Any = None, get_app_info: bool = False, host: Any = None, gdim: bool = True) -> GdimTemplate:
    """
    Get the table structure of a template according to the user token and the template id or the project id.
    
    Parameters
    ----------
    user_token: str
        The user token.
    
    tpl_id: int | str | None, default: None
        The template id.
        If not None, proj_id will be ignored.
    
    proj_id: int | str | None, default: None
        The project id.
    
    get_app_info: bool, default: False
        Only valid when gdim is True.
        Whether to get the application information.
    
    host: str | None, default: _default_host
        The host of the GDIM platform.
    
    gdim: bool, default: True
        Whether to get the data from GDIM. If False, the data is from GBIM.
    
    Returns
    -------
    GdimTemplate
        The structure of the template.
    """
    ...
def get_table_data(user_token: str, proj_id: Any, table_fields: dict[str, Any], main_key_values: Any = None, tpl_structure: Any = None, host: Any = None, gdim: bool = True) -> dict[str, GdimTableData]:
    """
    Get the data from tables in a project according to the user token, the project name, the table names and the field names.
    
    Parameters
    ----------
    user_token: str
        The user token.
    
    proj_id: int | str
        The project id (name).
    
    table_fields: dict[str, list[str]],
        The table names and the field names. Keys are table names and values are field names.
    
    main_key_values: dict[str, Any] | None, default: None
        The main key values of the parent table. Keys are table names and values are main key values.
        It's used when the table has a parent table.
        Only valid when gdim is True.
    
    tpl_structure: GdimTemplate | None, default: None
        The template structure. If None, the template structure will be fetched from GDIM.
    
    host: str | None, default: None
        The host of the GDIM platform.
    
    gdim: bool, default: True,
        Whether to get the data from GDIM. If False, the data is from GBIM.
    
    Returns
    -------
    dict[str, GdimTableData]
        The data from tables in a project. Keys are table names.
    """
    ...
def write_table_data(user_token: str, proj_id: Any, data: Any, raise_error: bool = True, table_names: Any = None, fields_mapping: Any = None, strict_datetime_validation: bool = True, validation_level: Literal[Any, Any] = "full", host: Any = None) -> dict[str, str]:
    """
    Write the data to tables in a project according to the user token, the project id and the data.
    
    Parameters
    ----------
    user_token: str
        The user token.
    
    proj_id: int | str
        The project id.
    
    data: TableData | pd.DataFrame | SingleResult | TableCollection
        The data to be written to the tables.
    
    raise_error: bool, default: True
        If True, the module will raise an error if the write operation is not successful.
        If False, the module will return the error message as a warning and continue to write the next table.
    
    table_names: str | list[str] | None, default: None
        The name or title of the table to be written to. table name will be checked at first, if not found, the title will be checked.
        If None, for `TableData` and `TableCollection`, the name of the table in the table data will used. Otherwise, the table name is neccessary.
        If not None, for `TableData` and `TableCollection`, the table name in the data will be ignored.
        If `list`, the data should be `TableCollection` and has the same length as the data.
    
    fields_mapping: dict[str, str] | dict[str, dict[str, str]] | None, default: None
        The mapping of the fields in the data to the fields in the gdim tables.
        If dict[str, str], the key can be either the column name or field title in the source data,
        and the value can be either the field name or field title in the GDIM tables.
        If dict[str, dict[str, str]], the key is the table name or title, the value is the corresponding
        field mapping where keys can be source column names or field titles, and values can be
        GDIM field names or field titles.
        If the mapping for a field in the data is not defined, the original field name or title will be used.
        If None, the fields in the data will be written to the gdim tables without any mapping using the original field names or titles.
    
        Examples:
        - {"source_col_name": "target_field_name"}  # Column name to field name
        - {"Source Title": "Target Title"}          # Field title to field title
        - {"source_col": "Target Title"}            # Column name to field title
        - {"Source Title": "target_field"}          # Field title to field name
    
    strict_datetime_validation: bool, default: True
        If True, use regex + datetime parsing validation (slower, more accurate).
        If False, use regex-only validation (faster, slightly less accurate).
        Default is True for data quality.
    
    validation_level: Literal["fast", "full"], default: "full"
        Validation complexity level:
        - "fast": Only vectorized operations + basic type checks (fastest)
        - "full": Complete validation including complex structure checks (slower, most accurate)
        Default is "full" for maximum data quality.
    
    host: str | None, default: None
        The host of the GDIM platform.
    
    Returns
    -------
    dict[str, str]
        The error message.
        The key is the table title, the value is the error message.
    """
    ...
def get_survey_statistics_table_title_name_map(pid: int, token: str, host: Any = None) -> dict[str, int]:
    """
    Get the survey statistics tables title and name map according to the project id and the token.
    
    Returns
    -------
        dict[str, int]
        key is the table title, value is the table id
    """
    ...
def get_survey_statistics_table_data(pid: int, ufid: int, token: str, host: Any = None) -> dict[str, Any]:
    """
    Get the survey statistics table data according to the project id and the table id.
    
    Parameters
    ----------
    pid: int
        The project id.
    
    ufid: int
        The table id.
    
    token: str
        The token.
    
    host: str, default: _default_host
        The host of the GDIM system.
    
    Returns
    -------
    dict[str, tuple[pd.DataFrame, list[str]]]
        The key is the table title, the value is a tuple of pandas DataFrame and field titles.
    """
    ...
def get_geo_params_table_title_name_map(pid: int, token: str, host: Any = None) -> dict[str, str]:
    """
    Get the geo params table field title and name map according to the project id and the token.
    获取「岩土参数建议值表」中字段标题和字段名的映射。
    
    Parameters
    ----------
    pid: int
        The project id.
    
    token: str
        The token.
    
    host: str, default: _default_host
        The host of the GDIM system.
    
    Returns
    -------
        dict[str, str]
        key is the field title, value is the field name
    """
    ...
def get_geo_params_table_data(pid: int, token: str, fid: int = 4197, host: Any = None) -> list[Any]:
    """
    Get the suggest table data  according to the project id 、fid and the token.
    
    Parameters
    ----------
    pid: int
        project's pid
    
    token: str
        The token.
    
    fid: int
        The id of the geo params table in database. It's fixed as 4197 at current version.
    
    host: str, default: _default_host
        The host of the GDIM system.
    
    Returns
    -------
        list[dict[str, Any]]
        The data is in row-wise format. Key is the field name, value is the field value.
    """
    ...
def get_report_text_library_list(token: str, host: Any = None) -> dict[str, Any]:
    """
    Get the report text library list according to the project id and the token.
    
    Parameters
    ----------
    pid: int
        project's pid
    
    token: str
        The token.
    
    host: str, default: _default_host
        The host of the GDIM system.
    
    Returns
    -------
        dict[str, dict[str, list[str] | dict[str, list[str]]]]
        Example:
        {
            "report_text_library_name": {
                "text": ["text_content_1", "text_content_2", ...],
                "document": {
                    "documents_path": ["document_path_1", "document_path_2", ...],
                    "documents_name": ["document_name_1", "document_name_2", ...]
                },
                "template": {
                    "templates_path": ["template_path_1", "template_path_2", ...],
                    "templates_name": ["template_name_1", "template_name_2", ...]
                }
        }
    """
    ...
def get_project_info_structure(pid: Any, token: str, host: Any = None) -> GdimTableMetaData:
    """
    Get the project structure according to the project id and the token.
    
    Parameters
    ----------
    pid: int | str
        project's pid
    
    token: str
        The token.
    
    host: str, default: _default_host
        The host of the GDIM system.
    
    Note
    ----
    This function is only availabe for gdim.
    """
    ...
def get_project_info(pid: Any, token: str, host: Any = None, gdim: bool = True) -> GdimProjectInfo:
    """
    Get the project info according to the project id and the token.
    
    Parameters
    ----------
    pid: int
        project's pid
    
    token: str
        The token.
    
    host: str, default: _default_host
        The host of the GDIM system.
    
    gdim: bool, default: True
        Whether to use the gdim api.
    """
    ...
def update_project_info(token: str, data: GdimProjectInfo, host: Any = None) -> Any:
    """Update the project info by the project id and the token."""
    ...
def get_proj_list(user_token: str, host: Any = None, scope: Literal[Any, Any, Any] = "all", dataTemplateId: Any = None, gdim: bool = True) -> dict[str, ProjMetadata]:
    """
    Get the list of projects according to the user token at current space.
    
    Parameters
    ----------
    user_token: str
        The user token.
    
    host: str | None, default: None
        The host of the GDIM platform.
    
    scope: Literal["all", "created", "joined"], default: "all"
        The scope of the projects. Only valid when `gdim` is True.
        - "all": All projects.
        - "created": Only projects that the user created.
        - "joined": Only projects that the user joined.
    
    dataTemplateId: str | int | None, default: None
        The id of the template. Only valid when `gdim` is True.
    
    Returns:
        dict[str, ProjMetadata]: The list of projects. Keys are both project names and project titles.
    """
    ...
def get_project_roles(user_token: Any = None, proj_name: Any = None, host: Any = None) -> Any:
    """Get the roles of a project according to the user token and the project name."""
    ...
def get_bores_coordinate(pid: int, token: str, host: Any = None) -> pd.DataFrame:
    """Get the xy coordinate and longitude latitude of the bores according to the project id and the token."""
    ...
def get_pipeline_json_to_db(token: str, pid: int, pipelineName: str, host: Any = None) -> list[Any]:
    """
    Get pipeline json_to_db data with enhanced metadata structure.
    
    Parameters
    ----------
    token : str
        Authentication token
    pid : int
        Project ID
    pipelineName : str
        Name of the pipeline
    host : str | None, optional
        API host URL
    
    Returns
    -------
    list[list[dict[str, Any]]]
        List of pipeline runs, where each run contains a list of data items.
        Each data item has the structure:
        {
            "name": str,           # port_name, attr_name, or param_name
            "data_type": str,      # "port", "pipeline_attr", "module_attr"
            "module_name": str | None,  # None for pipeline_attr
            "value_type": str,     # "TableData", "str", "int", "dict", etc.
            "data": Any,           # The actual serialized data
            "timestamp": str       # ISO format timestamp
        }
    
    Examples
    --------
    >>> result = get_pipeline_json_to_db(token, 123, "MyPipeline")
    >>> # Returns multiple runs:
    >>> [
    ...     [  # First run
    ...         {
    ...             "name": "OutputData",
    ...             "data_type": "port",
    ...             "module_name": "MyModule",
    ...             "value_type": "TableData",
    ...             "data": {...},
    ...             "timestamp": "2024-01-01T10:00:00Z"
    ...         },
    ...         {
    ...             "name": "workspace",
    ...             "data_type": "pipeline_attr",
    ...             "module_name": None,
    ...             "value_type": "str",
    ...             "data": "/path/to/workspace",
    ...             "timestamp": "2024-01-01T10:00:00Z"
    ...         }
    ...     ],
    ...     [  # Second run
    ...         {...}
    ...     ]
    ... ]
    """
    ...
def download_gdim_file(url: str, file_path: Any, host: Any = None) -> Path:
    """
    Download the file from the GDIM platform.
    
    Parameters
    ----------
    url: str
        The url of the file.
        If the url is not a full url, the host will be added automatically.
    
    file_path: str | Path
        The path to save the file. Can be:
        - Full path with filename: '/path/to/file.pdf'
        - Directory only: '/path/to/' - filename will be extracted from URL/response
        - Just a directory name: '/path/to' - filename will be extracted from URL/response
    
    host: str | None, default: None
        The host of the platform.
    
    Returns
    -------
    Path
        The actual path where the file was saved.
    """
    ...
def upload_file_to_gdim(user_token: str, pid: Any, file_path: Any = None, data: Any = None, filename: Any = None, upload_type: Literal[Any, Any] = "temp", host: Any = None) -> GdimMinIOFile:
    """
    Upload a file to the GDIM platform's MinIO server.
    
    Parameters
    ----------
    user_token: str
        The authentication token.
    
    pid: int | str
        The project id.
    
    file_path: str | Path | None, default: None
        The path to the file on disk to upload. If provided, data parameter will be ignored.
    
    data: bytes | None, default: None
        The binary data to upload as a file. Only used if file_path is None.
    
    filename: str | None, default: None
        The name to use for the uploaded file when uploading data directly.
        Required if file_path is None and data is provided.
    
    upload_type: Literal["temp", "app_auto_report"] = "temp",
        The type of the upload.
        "temp": upload to the temporary file server.
        "app_auto_report": upload to the application AutoReport file server.
    
    host: str | None, default: None
        The host URL of the GDIM platform.
    
    Returns
    -------
    GdimMinIOFile
        The GdimMinIOFile object containing file information from MinIO server.
    
    Raises
    ------
    ValueError
        If neither file_path nor data is provided, or if filename is missing when uploading data.
    requests.exceptions.RequestException
        If the upload request fails.
    Exception
        If the API returns an error.
    """
    ...
def write_mdb_from_minIO_file(user_token: str, pid: Any, mdbFileId: str, jsonFileId: str, host: Any = None) -> GdimMinIOFile:
    """
    Write a mdb file by a mdb template and json data from minIO file server.
    
    Parameters
    ----------
    user_token: str
        The authentication token.
    
    pid: int | str
        The project id.
    
    mdbFileId: str | Path
        The id of the mdb file.
    
    jsonFileId: str
        The id of the json file.
    
    Returns
    -------
    GdimMinIOFile
        The GdimMinIOFile object containing file information from MinIO server.
    """
    ...
def get_bore_type_mapping(user_token: str, pid: Any = None, tpl_id: Any = None, host: Any = None) -> dict[str, str]:
    """
    Get the bore type mapping between the bore_type in the original table and  from the GDIM platform.
    
    Parameters
    ----------
    user_token: str
        The authentication token.
    
    pid: int | str | None, default: None
        The project id.
        If not None, tpl_id will be ignored.
    
    tpl_id: int | str | None, default: None
        The template id.
    
    host: str | None, default: None
        The host URL of the GDIM platform.
    
    Returns
    -------
    dict[str, str]
        The bore type mapping between the bore_type in the original table and the bore_type in the GDIM platform.
        Example:
        {
            "机械钻孔": "取土井孔",
            "动探钻孔": "圆锥动力触探试验孔",
            "静探钻孔": "静力触探试验孔",
            "探坑": "探槽",
            "分层高程点": null,
            "虚拟点": null
        }
    """
    ...

class GdimTplMetadata(BaseModel):
    """The list of templates."""
    name: str
    title: str
    description: str | None

class ProjMetadata(BaseModel):
    """The metadata of a project."""
    name: str
    title: str
    tpl_name: str
    tpl_title: str
    space_name: str
    space_title: str

class GdimTableData(BaseModel):
    """The data from GDIM table."""
    table_metadata: GdimTableMetaData
    data: list[Any]
